package penguin.masking;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * DFA
 *
 * @author wuxh
 * @version 1.0.0
 */
public class SensitiveWordMasking {

    public static final String SYMBOL_NOTE = "#";
    public static final String SYMBOL_COLUMN = ":";
    public static final String SYMBOL_REGEX = "r:";

    private Set<String> columnPrivacy = null;
    private Set<String> regexPrivacy = null;
    private String user;
    private long lastModifiedTime = 0l;

    public SensitiveWordMasking(Set<String> words) {
        Set<String> lineSet = new HashSet<>();
        Set<String> _columnPrivacy = new HashSet<>();
        Set<String> regexSet = new HashSet<>();
        for (String word : words) {
            if (word.startsWith(SYMBOL_NOTE)) {
                continue;
            }
            if (word.startsWith(SYMBOL_REGEX)) {
                regexSet.add(word.substring(SYMBOL_REGEX.length()));
            } else if (word.indexOf(SYMBOL_COLUMN) != -1) {
                _columnPrivacy.add(word);
            } else {
                lineSet.add(word);
            }
        }
        this.columnPrivacy = _columnPrivacy;
        this.regexPrivacy = regexSet;
        buildTree(lineSet);
    }

    Map sensitiveWord = null;
    public static final String END = "end";

    public void buildTree(Set<String> words) {
        sensitiveWord = new HashMap<>();
        for (String word : words) {
            char[] chars = word.toCharArray();
            Map current = sensitiveWord;
            for (int i = 0; i < chars.length; i++) {
                char c = chars[i];
                if (!current.containsKey(c)) {
                    Map map = new HashMap();
                    map.put(END, false);
                    current.put(c, map);
                    current = map;
                } else {
                    current = (Map) current.get(c);
                }
                if (i == chars.length - 1) {
                    current.put(END, true);
                }
            }
        }
    }

    /**
     * 最大匹配算法
     *
     * @param text
     * @param fromIndex
     * @return
     */
    public int checkWord(String text, int fromIndex) {
        char[] chars = text.toCharArray();
        Map current = sensitiveWord;
        boolean isFind = false;
        int len = 0;
        int findLen = 0;
        for (int i = fromIndex; i < chars.length; i++) {
            char c = chars[i];
            Map map = (Map) current.get(c);
            if (map == null) {
                break;
            }
            len++;
            boolean isEnd = (boolean) map.get(END);
            if (isEnd) {
                isFind = true;
                findLen = len;
            }
            current = map;
        }
        return isFind ? findLen : 0;
    }

    public String filter(String text) {
        StringBuilder sb = new StringBuilder(text);
        int index = 0;
        int replaceNum = 0;
        while (index < text.length()) {
            int len = checkWord(text, index);
            if (len > 0) {
                String replaceContent = replaceContent(text, index, len);
                sb.replace(index - replaceNum, index - replaceNum + len, replaceContent);
                replaceNum += len - replaceContent.length();
                index += len;
            } else {
                index++;
            }
        }
        return sb.toString();
    }

    /**
     * 动态产生替换内容
     *
     * @param text
     * @param index
     * @param len
     * @return
     */
    public String replaceContent(String text, int index, int len) {
        String _content = text.substring(index, index + len);
        return MaskingContext.context().addReduction(_content);
    }

    public boolean haveColumnMasking() {
        return columnPrivacy != null && !columnPrivacy.isEmpty();
    }

    public boolean columnMasking(String tableName, String column) {
        return columnPrivacy.contains(":" + column) || columnPrivacy.contains(tableName + ":" + column);
    }

    public String regexMasking(String value) {
        if (regexPrivacy == null) return value;
        for (String s : regexPrivacy) {
            value = StringUtil.replace(value, s, new CallBack<String, String>() {
                @Override
                public String call(String group) {
                    return MaskingContext.context().addReduction(group);
                }
            });
        }
        return value;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public long getLastModifiedTime() {
        return lastModifiedTime;
    }

    public void setLastModifiedTime(long lastModifiedTime) {
        this.lastModifiedTime = lastModifiedTime;
    }
}
